package top.jnaw.jee.utils;

import com.alibaba.druid.filter.stat.StatFilter;
import com.alibaba.druid.wall.WallFilter;
import com.jfinal.config.Routes;
import com.jfinal.config.Routes.Route;
import com.jfinal.handler.Handler;
import com.jfinal.kit.PathKit;
import com.jfinal.kit.PropKit;
import com.jfinal.kit.StrKit;
import com.jfinal.plugin.IPlugin;
import com.jfinal.plugin.activerecord.ActiveRecordPlugin;
import com.jfinal.plugin.activerecord.IDataSourceProvider;
import com.jfinal.plugin.druid.DruidPlugin;
import com.jfinal.plugin.druid.DruidStatViewHandler;
import com.jfinal.plugin.redis.RedisPlugin;
import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.List;
import javax.sql.DataSource;
import top.jnaw.jee.plugin.shiro.ShiroKit;
import top.jnaw.jee.plugin.shiro.ShiroPlugin;

/**
 * Created by neo on 17-6-1.
 *
 * JFinal 插件生成器
 */
public class Generator {

  protected static final String[] DB_FORMATTERS = {
      "jdbc:mysql://%s:%d/%s?characterEncoding=%s&autoReconnected=true&zeroDateTimeBehavior=CONVERT_TO_NULL",
  };

  protected static final String[] DB_TYPE_NAMES = {
      "mysql",
  };

  /**
   * 生成 ORM 的 model 文件，依赖配文件中的 `model_package` 作为输出路径
   *
   * @param dataSource 数据源
   * @return 是否成功
   */
  public static boolean model(DataSource dataSource) {
    boolean result = false;
    final String modelPackage = PropKit.get("model_package").trim();

    if (null != modelPackage && modelPackage.length() > 0) {
      final String modelFolder =
          PathKit.getWebRootPath() + "/../../src/main/java/" + modelPackage
              .replaceAll("\\.", "\\/");

      final String baseFolder = modelFolder + "/base";
      final String basePackage = modelPackage + ".base";

      com.jfinal.plugin.activerecord.generator.Generator gen = new com.jfinal.plugin.activerecord.generator.Generator(
          dataSource, basePackage, baseFolder, modelPackage, modelFolder);

      gen.setGenerateDaoInModel(PropKit.getBoolean("generate_dao_in_model"));
      gen.setGenerateDataDictionary(PropKit.getBoolean
          ("generate_data_dictionary"));
      gen.setRemovedTableNamePrefixes("t_");

      gen.generate();
      result = true;
    }

    return result;
  }

  /**
   * 生成 Redis Plugin 对象，依赖的配置文件属性：
   *
   * <pre>
   * + redis_cache_name
   * + redis_host
   * + redis_port
   * + redis_timeout
   * + redis_pwd
   * </pre>
   */
  public static IPlugin cachePlugin() {
    RedisPlugin redis;

    final String redisPassword = PropKit.get("cache_pwd");
    if (StrKit.isBlank(redisPassword)) {
      redis = new RedisPlugin(PropKit.get("cache_name"),
          PropKit.get("cache_host"), PropKit.getInt("cache_port"),
          PropKit.getInt("cache_timeout"));
    } else {
      redis = new RedisPlugin(PropKit.get("project_name"),
          PropKit.get("cache_host"), PropKit.getInt("cache_port"),
          PropKit.getInt("cache_timeout"), redisPassword);
    }

    return redis;
  }

  private static DruidPlugin genDruidPlugin(DBType type, String host,
      int port, String name, String encode, String user, String password,
      int max, int min) {
    return genDruidPlugin(type, host, port, name, encode, user, password, max,
        min, null);
  }

  private static DruidPlugin genDruidPlugin(DBType type, String host,
      int port, String name, String encode, String user, String password,
      int max, int min, String validQuery) {
    final String jdbcUrl = String
        .format(DB_FORMATTERS[type.ordinal()], host, port, name, encode);

    DruidPlugin druid = new DruidPlugin(jdbcUrl, user, password);
    druid.setMaxActive(max);
    druid.setMinIdle(min);
    druid.setInitialSize(min);

    if (StrKit.notBlank(validQuery)) {
      druid.setValidationQuery(validQuery);
    }

    WallFilter wall = new WallFilter();
    wall.setDbType(DB_TYPE_NAMES[type.ordinal()]);
    druid.addFilter(wall);
    druid.addFilter(new StatFilter());
    druid.start();
    return druid;
  }

  /**
   * 生成 Druid Plugin 对象，最小连接数为 2，依赖的配置文件属性：
   *
   * <pre>
   * + db_host
   * + db_port
   * + db_name
   * + db_user
   * + db_pwd
   * + db_encode
   * + db_valid_query
   * </pre>
   */
  public static IDataSourceProvider dataSourcePlugin() {
    return genDruidPlugin(DBType.MYSQL, PropKit.get("db_host"),
        PropKit.getInt("db_port"), PropKit.get("db_name"),
        PropKit.get("db_encode"), PropKit.get("db_user"), PropKit.get("db_pwd"),
        10, 2, PropKit.get("db_valid_query", null));
  }

  /**
   * 生成指定配置的 Druid Plugin 对象，无需配置文件属性
   *
   * @param configName 配置名称
   * @param type 数据库类型
   * @param host 主机名
   * @param port 端口号
   * @param name 数据库名称
   * @param encode 数据库编码
   * @param user 数据库用户名
   * @param password 数据库密码
   * @param max 最大连接数
   * @param min 最小连接数
   */
  public static IDataSourceProvider dataSourcePluginWithConfigName(
      String configName, DBType type, String host, int port, String name,
      String encode, String user, String password, int max, int min) {
    DruidPlugin druid = genDruidPlugin(type, host, port, name, encode, user,
        password, max, min);
    ActiveRecordPlugin ar = new ActiveRecordPlugin(configName, druid);
    ar.start();
    return druid;
  }

  /**
   * 生成 Druid 统计处理器
   *
   * @param path 访问路径
   */
  public static Handler statHandler(String path) {
    return new DruidStatViewHandler(path);
  }

  /**
   * 生成 Shiro Plugin，用于权限管理
   *
   * @param dataSource 数据源
   * @param routes 全局路由
   */
  public static IPlugin shiroPlugin(DataSource dataSource, Routes routes) {
    ShiroPlugin shiro = null;

    List<Route> list = new ArrayList<>();
    list.addAll(routes.getRouteItemList());
    for (Routes r : Routes.getRoutesList()) {
      list.addAll(r.getRouteItemList());
    }

    boolean useSalt = PropKit.getBoolean("shiro_use_salt");
    String hashAlgorithm = PropKit.get("shiro_hash_algorithm");
    int hashIterations = PropKit.getInt("shiro_hash_iterations");
    String loginUrl = PropKit.get("shiro_redirect_login_url");
    String successUrl = PropKit.get("shiro_redirect_success_url");
    String unauthorizedUrl = PropKit.get("shiro_redirect_unauthorized_url");

    if (null != dataSource && list.size() > 0 && StrKit
        .notBlank(loginUrl, successUrl, unauthorizedUrl)) {
      shiro = new ShiroPlugin(list);
      ShiroKit.setDataSource(dataSource);
      ShiroKit.setUseSalt(useSalt);
      ShiroKit.setHashAlgorithmName(hashAlgorithm);
      ShiroKit.setHashIterations(hashIterations);
      ShiroKit.setLoginUrl(loginUrl);
      ShiroKit.setSuccessUrl(successUrl);
      ShiroKit.setUnauthorizedUrl(unauthorizedUrl);
      Log.i("shiro is online, useSalt: " + useSalt);
    } else {
      throw new InvalidParameterException();
    }

    return shiro;
  }

  /**
   * 数据库类型
   */
  public enum DBType {
    MYSQL,
  }
}
